import { createApp } from 'vue'
// 用来添加样式的方法，下面会看到
import { addClass, removeClass } from '@/assets/js/dom'
const relativeCls = 'g-relative'

export default function createLoadingLikeDirective(Comp) {
  // 返回一个对象，给app.directive('loading',{})
  return {
    // 在绑定元素的父组件及它自己的所有子节点挂载完后调用
    mounted (el, binding) {
      // 创建一个应用实例app, 应用实例可以多个，并不影响main.js中的app
      const app = createApp(Comp)  
      // 作用在于创建一个根组件，把Comp挂载到新的div中
      // instance 返回的就是根组件，也就是Comp组件实例，他是一个对象
      const instance = app.mount(document.createElement('div'))

      // 因为loading的状态是会更新的，所以才会有显示隐藏
      // 那么就要把我们的这个instance组件实例保存起来先
      // loading更新就会触发到updated钩子，把instance存到el中，那么
      // updated的参数el就可以访问到
      // name就是组件的name
      const name = Comp.name
      // el就是loading指令当前挂载到的节点
      if(!el[name]) {
        el[name] = {}
      }
      // 用name做区分，是为了如果有其他指令也用这个js文件，也不会重复
      el[name].instance = instance
      // 例如在 v-my-directive:foo 中，参数是 "foo"
      // 获取loading自定义的文本
      const title = binding.arg
      if(typeof title !== 'undefined') {
        // 调用loading组件的方法
        instance.setTitle(title)
      }
      // v-my-directive="true" 中，值是 true
      // 控制loading是否显示
      if(binding.value) {
        // 把loading添加到页面上
        append(el)
      }
    },
    // 更新钩子
    updated(el, binding){
      // 更新的时候， 获取loading自定义的文本
      const title = binding.arg
      const name = Comp.name
      if(typeof title !== 'undefined'){
        // 调用loading组件的方法
        // 其实这里和上面的写法一样的，只是调用instance时不一样
        // 因为我们在mounted时已经保存了一次
        el[name].instance.setTitle(title)
      }
      // 如果值不一致
      if(binding.value !== binding.oldValue){
        binding.value ? append(el) : remove(el)
      }
    },
  }
  // 添加到dom的函数
  function append(el){
    // 这里的el肯定是指令绑定到的元素   
    // 那么我们先来考虑一点。 loading组件时通过absolute定位上去的  
    // 如果父元素没有定位信息，那么loading可能就无法正常显示了
    const name = Comp.name
    // 获取到绑定元素的样式，看看它有没有定位信息
    const style = getComputedStyle(el)
    // 如果没有，那么就为它添加样式
    if(['absolute', 'fixed', 'relative'].indexOf(style.position)===-1){
      addClass(el, relativeCls)
    }
    el.appendChild(el[name].instance.$el)
  }
  // 移除父组件的定位
  function remove(el) {
    const name = Comp.name
    removeClass(el, relativeCls)
    el.removeChild(el[name].instance.$el)
  }
}

